/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 *
 *           File:  encdec_tb.h
 *    Description:  8b10b encoder and decoder testbeanch
 *       
 *        Created:  13.02.2013 16:03:28
 *         Author:  Aleksey Golovchenko (lexgolovchenko@gmail.com)
 *   Organization:  St.Petersburg State Polytecnical Universisy
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef encdec_tb_
#define encdec_tb_

#include <systemc.h>

#include "enc8b10b.h"
#include "dec8b10b.h"
#include "bsc.h"

SC_MODULE(tb_8b10b)
{
    // ------- Clock signals -----------------
    sc_in<bool> clk;
    
    // ------- Internal modules --------------    
    enc8b10b encoder;
    dec8b10b decoder;
    bsc channel;
    
    // ------- Encoder input signals ---------
    sc_signal<bool> enc_clk;
    sc_signal<bool> ctrl_in;
    sc_signal<bool> rd_reset;
    sc_signal<sc_uint<8> > data_in;

    // ------- Encoder output signals --------
    sc_signal<bool> rd_out;    

    // ------- Decoder input signals ---------
    sc_signal<bool> dec_clk;

    // ------- Decoder output signal ---------
    sc_signal<bool> decode_err;
    sc_signal<bool> ctrl_out;
    sc_buffer<sc_uint<8> > data_out;

    // ------- Connection to channel signal --
    sc_buffer<sc_uint<10> > ch_inp;
    sc_buffer<sc_uint<10> > ch_out;

    // ------- Trace file --------------------
    sc_trace_file *m_wf;

    SC_CTOR(tb_8b10b) :
        encoder("encoder_8b10b"),
        decoder("decoder_8b10b"),
        channel("channel", 0.005)
    {   
        // connect encoder signals     
        encoder.clk(enc_clk);
        encoder.ctrl(ctrl_in);
        encoder.rd_reset(rd_reset);
        encoder.data_in(data_in);
        encoder.rd_out(rd_out);
        
        // connect decoder signal
        decoder.clk(dec_clk);
        decoder.err(decode_err);
        decoder.ctrl(ctrl_out);
        decoder.data_out(data_out);

        // connect encoder to decoder
        encoder.enc_out(ch_inp);
        channel.inp(ch_inp);
        decoder.enc_inp(ch_out);
        channel.out(ch_out);

        // vcd trace signal
        m_wf = sc_create_vcd_trace_file("tb_8b10b");
        sc_trace(m_wf, clk, "clk");

        sc_trace(m_wf, enc_clk, "enc_clk"); 
        sc_trace(m_wf, ctrl_in, "enc_ctrl");
        sc_trace(m_wf, rd_reset, "enc_rd_reset");
        sc_trace(m_wf, data_in, "enc_data_in");
        sc_trace(m_wf, rd_out, "enc_rd_out");
 
        sc_trace(m_wf, ch_inp, "ch_inp");
        sc_trace(m_wf, ch_out, "ch_out");

        sc_trace(m_wf, dec_clk, "dec_clk");
        sc_trace(m_wf, decode_err, "dec_err");
        sc_trace(m_wf, ctrl_out, "dec_ctrl");
        sc_trace(m_wf, data_out, "dec_data_out");

        SC_THREAD(clock_generate);
        dont_initialize();
        sensitive << clk.pos() << clk.neg();

        SC_THREAD(input_data_generate);
        dont_initialize();
        sensitive << clk.pos();

        SC_THREAD(monitor);
        dont_initialize();
        sensitive << data_out;
    }    

    virtual ~tb_8b10b()
    {
        sc_close_vcd_trace_file(m_wf);
    }

    // ------- Generate clk for modules -----
    void clock_generate()
    {    
        while (1) {    
            wait();

            wait(5, SC_US);
            enc_clk = clk.read();

            wait(5, SC_US);
            dec_clk = clk.read();
        }
    }

    // ------- Generate data for encoder ----
    void input_data_generate() 
    {                       
        // 1. Generate only data symbols -> ctrl_in = 0
        unsigned char test_data = 255;
        while (test_data--) {                            
            wait();     
    
            ctrl_in.write(0);
            data_in.write(test_data);
        }       

        // 2. Generate control symbols -> ctrl_in = 1        
        const char cmd[][9] = {
            DEC_K28D0, DEC_K28D1, DEC_K28D2, DEC_K28D3, DEC_K28D4,
            DEC_K28D5, DEC_K28D6, DEC_K28D7, DEC_K23D7, DEC_K27D7,
            DEC_K29D7, DEC_K30D7
        };

        for (int i = 0; i < 12; ++i) {
            wait();
            ctrl_in.write(1);
            data_in.write(sc_bv<8>(cmd[i]));
        }

        sc_stop();
    }    

    // ------- Monitoring output value ------
    void monitor() 
    {                    
        printf("%-8s | %6s | %3s | %11s | %6s | %3s | %3s | %6s \n"
               "--------------------------------------------------------------------\n",
               "time", "input", "cmd", "channel  ", "output", "cmd", "err", "biterr");

        // 1. Wait 255 data symbols and 12 control symbols
        int cntr = 255 + 12;
        while (cntr--) {
            wait();        

            if (data_out.read() != data_in.read() 
                || ctrl_in.read() != ctrl_out.read() ) {            
                
                // for check that error is found
                int err_found_flag = (data_out.read() == sc_uint<8>(0) && decode_err.read()) ? 1 : 0 ;
                
                // if error not found, calculate amount of bit-error between input and output
                int bit_err_cntr = 0;
                sc_uint<8> data_err;
                if (!err_found_flag) {
                    data_err = data_in.read() ^ data_out.read();
                    for (int i = 0; i < 8; ++i) {
                        if (data_err[i]) {
                            ++bit_err_cntr;
                        }
                    }
                }

                // display information about error
                printf("%-8s | %6.2X | %3d | %3.3X <-> %3.3X | %6.2X | %-3d | %-3c | %-6d \n",
                        sc_time_stamp().to_string().c_str(), 
                        data_in.read().to_uint(), 
                        ctrl_in.read(),
                        ch_inp.read().to_uint(), 
                        ch_out.read().to_uint(), 
                        data_out.read().to_uint(), 
                        ctrl_out.read(), 
                        (err_found_flag) ? '+' : '-',
                        bit_err_cntr);
            }
        }    
    }
};

#endif

